namespace graver.CodeAnalysis.Syntax;

/// <summary>
/// 语法分析器
/// </summary>
internal sealed class Parser
{
    /// <summary>
    /// 解析的 Token 列表
    /// </summary>
    private readonly SyntaxToken[] _tokens;

    /// <summary>
    /// 当前 Token 位置指针
    /// </summary>
    private int _position;

    /// <summary>
    /// 诊断信息
    /// </summary>
    private readonly List<string> _diagnostics;

    public IEnumerable<string> Diagnostics => _diagnostics;

    public Parser(string text)
    {
        var tokens = new List<SyntaxToken>();
        var lexer = new Lexer(text);
        SyntaxToken token;
        do
        {
            token = lexer.Lex();
            if (token.Kind is SyntaxKind.BadToken or SyntaxKind.SpaceToken)
            {
                continue; // TODO 错误处理
            }

            tokens.Add(token);
        } while (SyntaxKind.EndOfFileToken != token.Kind);

        _tokens = tokens.ToArray();
        _position = 0;
        _diagnostics = new List<string>();
        _diagnostics.AddRange(lexer.Diagnostics);
    }

    private SyntaxToken Peek(int offset)
    {
        var index = _position + offset;
        if (index >= _tokens.Length)
        {
            return _tokens[_tokens.Length - 1];
        }

        return _tokens[index];
    }

    /// <summary>
    /// 当前 Token
    /// </summary>
    private SyntaxToken CurrentToken => Peek(0);

    /// <summary>
    /// 获取当前 Token , 并使 Token 位置指针向后移
    /// </summary>
    private SyntaxToken NextToken()
    {
        var token = CurrentToken;
        _position++;
        return token;
    }

    /// <summary>
    /// 检查当前 Token 是否是指定的类型
    /// </summary>
    /// <param name="kind">待匹配的 Token 类型</param>
    /// <returns></returns>
    private SyntaxToken MatchToken(SyntaxKind kind)
    {
        if (kind == CurrentToken.Kind)
        {
            return NextToken();
        }

        _diagnostics.Add($"Error: Unexpected token <{CurrentToken.Kind}>, expected <{kind}>");
        return new SyntaxToken(kind, CurrentToken.Position, null, null);
    }

    /// <summary>
    /// 此法解析器入口
    /// </summary>
    /// <returns>语法树</returns>
    public SyntaxTree Parse()
    {
        var expression = ParseExpression();
        var endOfFileToken = MatchToken(SyntaxKind.EndOfFileToken);
        return new SyntaxTree(_diagnostics, expression, endOfFileToken);
    }

    private ExpressionSyntax ParseExpression(int parentPrecedence = 0)
    {
        ExpressionSyntax left;

        var unaryOperatorPrecedence = CurrentToken.Kind.GetUnaryOperatorPrecedence();
        if (0 != unaryOperatorPrecedence && unaryOperatorPrecedence >= parentPrecedence)
        {
            var operatorToken = NextToken();
            var operand = ParseExpression(unaryOperatorPrecedence);
            left = new UnaryExpressionSyntax(operatorToken, operand);
        }
        else
        {
            left = ParsePrimaryExpressionNode();
        }

        while (true)
        {
            var precedence = CurrentToken.Kind.GetBinaryOperatorPrecedence();
            if (0 == precedence || precedence <= parentPrecedence)
            {
                break;
            }

            var operatorToken = NextToken();
            var right = ParseExpression(precedence);
            left = new BinaryExpressionSyntax(left, operatorToken, right);
        }

        return left;
    }

    /// <summary>
    /// 解析带括号的表达式
    /// </summary>
    private ExpressionSyntax ParsePrimaryExpressionNode()
    {
        switch (CurrentToken.Kind)
        {
            case SyntaxKind.OpenParenthesisToken:
            {
                var left = NextToken();
                var expression = ParseExpression();
                var right = MatchToken(SyntaxKind.CloseParenthesisToken);
                return new ParenthesizedExpressionSyntax(left, expression, right);
            }
            case SyntaxKind.KeyWordTrue:
            case SyntaxKind.KeyWordFalse:
            {
                var keywordToken = NextToken();
                var value = keywordToken.Kind == SyntaxKind.KeyWordTrue;
                return new LiteralExpressionSyntax(keywordToken, value);
            }
            default:
            {
                var numberToken = MatchToken(SyntaxKind.NumberToken);
                return new LiteralExpressionSyntax(numberToken);
            }
        }
    }
}
